import java.util.Arrays;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

// java PrimeC2
public class PrimeC2 {
	static final Thread.Builder.OfVirtual threadFactory = Thread.ofVirtual();

	static final class IntChan {
		static final class ThreadValueNode {
			final Thread thread = Thread.currentThread();
			int value;
		}

		private final ReentrantLock lock = new ReentrantLock();
		private ThreadValueNode[] waitNodes = new ThreadValueNode[2];
		private int waitCount;
		private boolean hasValue;

		private ThreadValueNode addWaitQueue() {
			var i = waitCount;
			if (i == waitNodes.length)
				waitNodes = Arrays.copyOf(waitNodes, i + i);
			var node = new ThreadValueNode();
			waitNodes[i] = node;
			waitCount = i + 1;
			return node;
		}

		private ThreadValueNode pollWaitNode() {
			var n = waitCount;
			if (n == 0)
				return null;
			waitCount = --n;
			return waitNodes[n];
		}

		void put(int v) {
			lock.lock();
			if (!hasValue) {
				var node = pollWaitNode();
				if (node != null) {
					lock.unlock();
					node.value = v;
					LockSupport.unpark(node.thread);
					return;
				}
				hasValue = true;
			}
			addWaitQueue().value = v;
			lock.unlock();
			LockSupport.park();
		}

		int take() {
			lock.lock();
			if (hasValue) {
				var node = pollWaitNode();
				if (node != null) {
					lock.unlock();
					LockSupport.unpark(node.thread);
					return node.value;
				}
				hasValue = false;
			}
			var node = addWaitQueue();
			lock.unlock();
			int v;
			do {
				LockSupport.park();
				v = node.value;
			} while (v == 0); // 这里需要判断value的有效性,因为有时会出现park不暂停的情况(可能被其它某处unpark过)
			return v;
		}
	}

	static void go(Runnable r) {
		threadFactory.start(r);
	}

	static void generate(IntChan ch) {
		try {
			//noinspection InfiniteLoopStatement
			for (int i = 2; ; i++)
				ch.put(i);
		} catch (Exception e) {
			//noinspection CallToPrintStackTrace
			e.printStackTrace();
		}
	}

	static void filter(IntChan in, IntChan out, int prime) {
		try {
			//noinspection InfiniteLoopStatement
			for (; ; ) {
				var i = in.take();
				if (i % prime != 0)
					out.put(i);
			}
		} catch (Exception e) {
			//noinspection CallToPrintStackTrace
			e.printStackTrace();
		}
	}

	public static void main(String[] args) throws InterruptedException {
		var count = args.length > 0 ? Integer.parseInt(args[0]) : 10000;
		go(() -> {
			var ch = new IntChan();
			var ch0 = ch;
			go(() -> generate(ch0));
			try {
				for (int i = 0; ; ) {
					var prime = ch.take();
					if (++i == count) {
						System.out.println(prime);
						System.exit(0);
					}
					var ch1 = ch;
					var ch2 = new IntChan();
					go(() -> filter(ch1, ch2, prime));
					ch = ch2;
				}
			} catch (Exception e) {
				//noinspection CallToPrintStackTrace
				e.printStackTrace();
			}
		});
		Thread.sleep(1_000_000L);
	}
}
